#!/bin/bash
# cmdparser parse command line arguments with Bash built-in regex matching
# source: http://codesnippets.joyent.us/posts/show/2054?format=rss
# Author: jv
# License: The MIT License, Copyright (c) 2009 jv
#
# ipbl command args processing file
# please refer for README for help
#
# ipbl: A blocking tool of hooligans IPs using iptables, with a blacklist and whilelist files
# Copyright (C) 2012 ArabOSG, http://arabosg.org
# Project url: http://ipbl.projects.arabosg.org
# Author: Mohammed Shannaq , http://irbidnet.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# process command-line arguments
#--------------------------------
#this file suppose that (flags) and (switches) are declare
#for example
#flags="(d|a)"
#declare d a
#switches="(p|u|fresh|blit)"
#declare p u fresh blit 
#--------------------------------

#for testing comment the following line in real world
#printf "%s\n" "$@" | nl

declare argstr argvar argvar_escaped char flagvar optstr piped pipedstr       # script variables
declare -i optid pipedvar

# piped="piped" will be used for variable creation 
# example: piped="piped"; pipedstr="piped arg"; eval $piped='"$(echo "$pipedstr")"'; echo "$piped"
piped="piped"

# default value is set to "no pipe"
pipedvar=0
pipedstr=""

# if /dev/stdin has a size greater than zero ...
if [[ -s /dev/stdin ]]; then pipedstr="$(</dev/stdin)"; fi 

if [[ $# -eq 0 ]] && [[ -z "$pipedstr" ]]; then
  printf "\n%s\n\n%s\n\n" 'No arguments specified!' "$usage" 1>&2
  exit 1
fi 

if [[ $# -eq 0 ]] && [[ -n "$pipedstr" ]]; then
  eval $piped='"${pipedstr}"'  
  pipedvar=1
fi 

# if there are command line arguments ...
# Note that $pipedvar may still be set to 1 below if the special read-from-stdin switch "-" is given.

if [[ $pipedvar -eq 0 ]]; then

   optstr=" "  
   optid=0

   while [[ -n "$optstr" ]]; do     

      # try to extract valid flags or switches from positional parameter $1
      # $1 gets shifted afterwards (cf. help shift)


      [[ "$1" =~ ^--?${flags}$ ]]
      optstr="${BASH_REMATCH[0]}"


      if [[ -n "$optstr" ]]; then optid=1; fi

      if [[ -z "$optstr" ]]; then optid=2;  [[ "$1" =~ ^--?${switches}$ ]]; optstr="${BASH_REMATCH[0]}"; fi

      if [[ -z "$optstr" ]]; then optid=3; [[ "$1" =~ ^--?${switches}+$ ]]; optstr="${BASH_REMATCH[0]}"; fi

      if [[ -z "$optstr" ]]; then optid=4; [[ "$1" =~ ^--?(${flags}=.*|${flags}[^[:space:]]+)$ ]]; optstr="${BASH_REMATCH[0]}"; fi


      if [[ -z "$optstr" ]]; then  
         if [[ "$1" = "-" ]] && [[ "$@" = "-" ]]; then  
            optid=5
            optstr="-" 
         fi
      fi


      if [[ -z "$optstr" ]]; then

         # append a space to each command line argument
         argstr="$(printf "%s" "${@/%/ }")"

         [[ "$argstr" =~ [[:space:]]--?(${flags}|${switches}) ]]

         if [[ -n "${BASH_REMATCH[0]}" ]]; then 
            printf "\n%s\x21\n\n%s\n\n%s\n\n" "Undefined non-option string: ${1:0:1000} is followed by a legal flag or switch" "${BASH_REMATCH[0]}" "$usage" 1>&2
            exit 1
         fi

      fi


      if [[ "$1" = "--" ]]; then shift; break; fi     # -- marks end of options

      if [[ -z "$optstr" ]]; then break; fi     # no further flags or switches to process


      # flag followed by space (example: -f file)
      if [[ $optid -eq 1 ]]; then 

         if [[ -z "$2" ]]; then
            printf "%s\n%s\n" "no argument given to flag: ${1} " "$usage" 1>&2
            exit 1
         fi 

         flagvar="${1#"${1%%[!-]*}"}"     # remove leading - or --
         argvar="$2"
         eval $flagvar='"${argvar}"'
         shift 2     # shift positional parameters $1 & $2 (that is, a flag plus its argument)
         continue


      # single switch (example: -a)
      elif [[ $optid -eq 2 ]]; then
         flagvar="${1#"${1%%[!-]*}"}"
         eval $flagvar='"1"'
         shift
         continue
  

      # combined switch (example: -abcc)
      elif [[ $optid -eq 3 ]]; then

         flagvar="${1#"${1%%[!-]*}"}"

         while [[ -n "$flagvar" ]]; do

            [[ "$flagvar" =~ ^${switches}.*$ ]]
            char="${BASH_REMATCH[1]}"

            eval $char='"1"'

            [[ "$flagvar" =~ ^${switches}(.*)$ ]]
            flagvar="${BASH_REMATCH[2]}"

         done

         shift
         continue


      # flag without following space (example: -ffile)
      elif [[ $optid -eq 4 ]]; then 

         [[ "${1#"${1%%[!-]*}"}" =~ ^${flags}=?(.*)$ ]]
         argvar="${BASH_REMATCH[2]}"

         [[ "${1#"${1%%[!-]*}"}" =~ ^${flags}=?.*$ ]]
         flagvar="${BASH_REMATCH[1]}"

         # alternative
         #[[ "${1}" =~ ^--?${flags}=?(.*)$ ]]
         #argvar="${BASH_REMATCH[2]}"

         #[[ "${1}" =~ ^--?${flags}=?.*$ ]]
         #flagvar="${BASH_REMATCH[1]}"

         eval $flagvar='"${argvar}"'
         shift
         continue


      # the special read-from-stdin switch "-"
      elif [[ $optid -eq 5 ]]; then 
         pipedvar=1
         eval $piped='"${pipedstr}"'
         shift
         break

      fi

      # remove positional parameter $1 from "$@"
      shift

   done

fi   # if [[$pipedvar -eq 0 ]]; then ...